home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-9.10-netbook-remix-PL.iso / casper / filesystem.squashfs / usr / lib / firefox-3.5.5 / components / FeedWriter.js < prev    next >
Text File  |  2009-11-09  |  49KB  |  1,388 lines

  1. //@line 42 "/build/buildd/firefox-3.5-3.5.5+nobinonly/build-tree/mozilla/browser/components/feeds/src/FeedWriter.js"
  2.  
  3. const Cc = Components.classes;
  4. const Ci = Components.interfaces;
  5. const Cr = Components.results;
  6. const Cu = Components.utils;
  7.  
  8. Cu.import("resource://gre/modules/XPCOMUtils.jsm");
  9.  
  10. function LOG(str) {
  11.   var prefB = Cc["@mozilla.org/preferences-service;1"].
  12.               getService(Ci.nsIPrefBranch);
  13.  
  14.   var shouldLog = false;
  15.   try {
  16.     shouldLog = prefB.getBoolPref("feeds.log");
  17.   } 
  18.   catch (ex) {
  19.   }
  20.  
  21.   if (shouldLog)
  22.     dump("*** Feeds: " + str + "\n");
  23. }
  24.  
  25. /**
  26.  * Wrapper function for nsIIOService::newURI.
  27.  * @param aURLSpec
  28.  *        The URL string from which to create an nsIURI.
  29.  * @returns an nsIURI object, or null if the creation of the URI failed.
  30.  */
  31. function makeURI(aURLSpec, aCharset) {
  32.   var ios = Cc["@mozilla.org/network/io-service;1"].
  33.             getService(Ci.nsIIOService);
  34.   try {
  35.     return ios.newURI(aURLSpec, aCharset, null);
  36.   } catch (ex) { }
  37.  
  38.   return null;
  39. }
  40.  
  41. const XML_NS = "http://www.w3.org/XML/1998/namespace"
  42. const HTML_NS = "http://www.w3.org/1999/xhtml";
  43. const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
  44. const TYPE_MAYBE_FEED = "application/vnd.mozilla.maybe.feed";
  45. const TYPE_MAYBE_AUDIO_FEED = "application/vnd.mozilla.maybe.audio.feed";
  46. const TYPE_MAYBE_VIDEO_FEED = "application/vnd.mozilla.maybe.video.feed";
  47. const URI_BUNDLE = "chrome://browser/locale/feeds/subscribe.properties";
  48. const SUBSCRIBE_PAGE_URI = "chrome://browser/content/feeds/subscribe.xhtml";
  49.  
  50. const PREF_SELECTED_APP = "browser.feeds.handlers.application";
  51. const PREF_SELECTED_WEB = "browser.feeds.handlers.webservice";
  52. const PREF_SELECTED_ACTION = "browser.feeds.handler";
  53. const PREF_SELECTED_READER = "browser.feeds.handler.default";
  54.  
  55. const PREF_VIDEO_SELECTED_APP = "browser.videoFeeds.handlers.application";
  56. const PREF_VIDEO_SELECTED_WEB = "browser.videoFeeds.handlers.webservice";
  57. const PREF_VIDEO_SELECTED_ACTION = "browser.videoFeeds.handler";
  58. const PREF_VIDEO_SELECTED_READER = "browser.videoFeeds.handler.default";
  59.  
  60. const PREF_AUDIO_SELECTED_APP = "browser.audioFeeds.handlers.application";
  61. const PREF_AUDIO_SELECTED_WEB = "browser.audioFeeds.handlers.webservice";
  62. const PREF_AUDIO_SELECTED_ACTION = "browser.audioFeeds.handler";
  63. const PREF_AUDIO_SELECTED_READER = "browser.audioFeeds.handler.default";
  64.  
  65. const PREF_SHOW_FIRST_RUN_UI = "browser.feeds.showFirstRunUI";
  66.  
  67. const TITLE_ID = "feedTitleText";
  68. const SUBTITLE_ID = "feedSubtitleText";
  69.  
  70. function getPrefAppForType(t) {
  71.   switch (t) {
  72.     case Ci.nsIFeed.TYPE_VIDEO:
  73.       return PREF_VIDEO_SELECTED_APP;
  74.  
  75.     case Ci.nsIFeed.TYPE_AUDIO:
  76.       return PREF_AUDIO_SELECTED_APP;
  77.  
  78.     default:
  79.       return PREF_SELECTED_APP;
  80.   }
  81. }
  82.  
  83. function getPrefWebForType(t) {
  84.   switch (t) {
  85.     case Ci.nsIFeed.TYPE_VIDEO:
  86.       return PREF_VIDEO_SELECTED_WEB;
  87.  
  88.     case Ci.nsIFeed.TYPE_AUDIO:
  89.       return PREF_AUDIO_SELECTED_WEB;
  90.  
  91.     default:
  92.       return PREF_SELECTED_WEB;
  93.   }
  94. }
  95.  
  96. function getPrefActionForType(t) {
  97.   switch (t) {
  98.     case Ci.nsIFeed.TYPE_VIDEO:
  99.       return PREF_VIDEO_SELECTED_ACTION;
  100.  
  101.     case Ci.nsIFeed.TYPE_AUDIO:
  102.       return PREF_AUDIO_SELECTED_ACTION;
  103.  
  104.     default:
  105.       return PREF_SELECTED_ACTION;
  106.   }
  107. }
  108.  
  109. function getPrefReaderForType(t) {
  110.   switch (t) {
  111.     case Ci.nsIFeed.TYPE_VIDEO:
  112.       return PREF_VIDEO_SELECTED_READER;
  113.  
  114.     case Ci.nsIFeed.TYPE_AUDIO:
  115.       return PREF_AUDIO_SELECTED_READER;
  116.  
  117.     default:
  118.       return PREF_SELECTED_READER;
  119.   }
  120. }
  121.  
  122. /**
  123.  * Converts a number of bytes to the appropriate unit that results in a
  124.  * number that needs fewer than 4 digits
  125.  *
  126.  * @return a pair: [new value with 3 sig. figs., its unit]
  127.   */
  128. function convertByteUnits(aBytes) {
  129.   var units = ["bytes", "kilobyte", "megabyte", "gigabyte"];
  130.   let unitIndex = 0;
  131.  
  132.   // convert to next unit if it needs 4 digits (after rounding), but only if
  133.   // we know the name of the next unit
  134.   while ((aBytes >= 999.5) && (unitIndex < units.length - 1)) {
  135.     aBytes /= 1024;
  136.     unitIndex++;
  137.   }
  138.  
  139.   // Get rid of insignificant bits by truncating to 1 or 0 decimal points
  140.   // 0 -> 0; 1.2 -> 1.2; 12.3 -> 12.3; 123.4 -> 123; 234.5 -> 235
  141.   aBytes = aBytes.toFixed((aBytes > 0) && (aBytes < 100) ? 1 : 0);
  142.  
  143.   return [aBytes, units[unitIndex]];
  144. }
  145.  
  146. function FeedWriter() {}
  147. FeedWriter.prototype = {
  148.   _mimeSvc      : Cc["@mozilla.org/mime;1"].
  149.                   getService(Ci.nsIMIMEService),
  150.  
  151.   _getPropertyAsBag: function FW__getPropertyAsBag(container, property) {
  152.     return container.fields.getProperty(property).
  153.                      QueryInterface(Ci.nsIPropertyBag2);
  154.   },
  155.  
  156.   _getPropertyAsString: function FW__getPropertyAsString(container, property) {
  157.     try {
  158.       return container.fields.getPropertyAsAString(property);
  159.     }
  160.     catch (e) {
  161.     }
  162.     return "";
  163.   },
  164.  
  165.   _setContentText: function FW__setContentText(id, text) {
  166.     this._contentSandbox.element = this._document.getElementById(id);
  167.     this._contentSandbox.textNode = this._document.createTextNode(text);
  168.     var codeStr =
  169.       "while (element.hasChildNodes()) " +
  170.       "  element.removeChild(element.firstChild);" +
  171.       "element.appendChild(textNode);";
  172.     Cu.evalInSandbox(codeStr, this._contentSandbox);
  173.     this._contentSandbox.element = null;
  174.     this._contentSandbox.textNode = null;
  175.   },
  176.  
  177.   /**
  178.    * Safely sets the href attribute on an anchor tag, providing the URI 
  179.    * specified can be loaded according to rules. 
  180.    * @param   element
  181.    *          The element to set a URI attribute on
  182.    * @param   attribute
  183.    *          The attribute of the element to set the URI to, e.g. href or src
  184.    * @param   uri
  185.    *          The URI spec to set as the href
  186.    */
  187.   _safeSetURIAttribute: 
  188.   function FW__safeSetURIAttribute(element, attribute, uri) {
  189.     var secman = Cc["@mozilla.org/scriptsecuritymanager;1"].
  190.                  getService(Ci.nsIScriptSecurityManager);    
  191.     const flags = Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL;
  192.     try {
  193.       secman.checkLoadURIStrWithPrincipal(this._feedPrincipal, uri, flags);
  194.       // checkLoadURIStrWithPrincipal will throw if the link URI should not be
  195.       // loaded, either because our feedURI isn't allowed to load it or per
  196.       // the rules specified in |flags|, so we'll never "linkify" the link...
  197.     }
  198.     catch (e) {
  199.       // Not allowed to load this link because secman.checkLoadURIStr threw
  200.       return;
  201.     }
  202.  
  203.     this._contentSandbox.element = element;
  204.     this._contentSandbox.uri = uri;
  205.     var codeStr = "element.setAttribute('" + attribute + "', uri);";
  206.     Cu.evalInSandbox(codeStr, this._contentSandbox);
  207.   },
  208.  
  209.   /**
  210.    * Use this sandbox to run any dom manipulation code on nodes which
  211.    * are already inserted into the content document.
  212.    */
  213.   __contentSandbox: null,
  214.   get _contentSandbox() {
  215.     if (!this.__contentSandbox)
  216.       this.__contentSandbox = new Cu.Sandbox(this._window);
  217.  
  218.     return this.__contentSandbox;
  219.   },
  220.  
  221.   /**
  222.    * Calls doCommand for a the given XUL element within the context of the
  223.    * content document.
  224.    *
  225.    * @param aElement
  226.    *        the XUL element to call doCommand() on.
  227.    */
  228.   _safeDoCommand: function FW___safeDoCommand(aElement) {
  229.     this._contentSandbox.element = aElement;
  230.     Cu.evalInSandbox("element.doCommand();", this._contentSandbox);
  231.     this._contentSandbox.element = null;
  232.   },
  233.  
  234.   __faviconService: null,
  235.   get _faviconService() {
  236.     if (!this.__faviconService)
  237.       this.__faviconService = Cc["@mozilla.org/browser/favicon-service;1"].
  238.                               getService(Ci.nsIFaviconService);
  239.  
  240.     return this.__faviconService;
  241.   },
  242.  
  243.   __bundle: null,
  244.   get _bundle() {
  245.     if (!this.__bundle) {
  246.       this.__bundle = Cc["@mozilla.org/intl/stringbundle;1"].
  247.                       getService(Ci.nsIStringBundleService).
  248.                       createBundle(URI_BUNDLE);
  249.     }
  250.     return this.__bundle;
  251.   },
  252.  
  253.   _getFormattedString: function FW__getFormattedString(key, params) {
  254.     return this._bundle.formatStringFromName(key, params, params.length);
  255.   },
  256.   
  257.   _getString: function FW__getString(key) {
  258.     return this._bundle.GetStringFromName(key);
  259.   },
  260.  
  261.   /* Magic helper methods to be used instead of xbl properties */
  262.   _getSelectedItemFromMenulist: function FW__getSelectedItemFromList(aList) {
  263.     var node = aList.firstChild.firstChild;
  264.     while (node) {
  265.       if (node.localName == "menuitem" && node.getAttribute("selected") == "true")
  266.         return node;
  267.  
  268.       node = node.nextSibling;
  269.     }
  270.  
  271.     return null;
  272.   },
  273.  
  274.   _setCheckboxCheckedState: function FW__setCheckboxCheckedState(aCheckbox, aValue) {
  275.     // see checkbox.xml, xbl bindings are not applied within the sandbox!
  276.     this._contentSandbox.checkbox = aCheckbox;
  277.     var codeStr;
  278.     var change = (aValue != (aCheckbox.getAttribute('checked') == 'true'));
  279.     if (aValue)
  280.       codeStr = "checkbox.setAttribute('checked', 'true'); ";
  281.     else
  282.       codeStr = "checkbox.removeAttribute('checked'); ";
  283.  
  284.     if (change) {
  285.       this._contentSandbox.document = this._document;
  286.       codeStr += "var event = document.createEvent('Events'); " +
  287.                  "event.initEvent('CheckboxStateChange', true, true);" +
  288.                  "checkbox.dispatchEvent(event);"
  289.     }
  290.  
  291.     Cu.evalInSandbox(codeStr, this._contentSandbox);
  292.   },
  293.  
  294.    /**
  295.    * Returns a date suitable for displaying in the feed preview. 
  296.    * If the date cannot be parsed, the return value is "false".
  297.    * @param   dateString
  298.    *          A date as extracted from a feed entry. (entry.updated)
  299.    */
  300.   _parseDate: function FW__parseDate(dateString) {
  301.     // Convert the date into the user's local time zone
  302.     dateObj = new Date(dateString);
  303.  
  304.     // Make sure the date we're given is valid.
  305.     if (!dateObj.getTime())
  306.       return false;
  307.  
  308.     var dateService = Cc["@mozilla.org/intl/scriptabledateformat;1"].
  309.                       getService(Ci.nsIScriptableDateFormat);
  310.     return dateService.FormatDateTime("", dateService.dateFormatLong, dateService.timeFormatNoSeconds,
  311.                                       dateObj.getFullYear(), dateObj.getMonth()+1, dateObj.getDate(),
  312.                                       dateObj.getHours(), dateObj.getMinutes(), dateObj.getSeconds());
  313.   },
  314.  
  315.   /**
  316.    * Returns the feed type.
  317.    */
  318.   __feedType: null,
  319.   _getFeedType: function FW__getFeedType() {
  320.     if (this.__feedType != null)
  321.       return this.__feedType;
  322.  
  323.     try {
  324.       // grab the feed because it's got the feed.type in it.
  325.       var container = this._getContainer();
  326.       var feed = container.QueryInterface(Ci.nsIFeed);
  327.       this.__feedType = feed.type;
  328.       return feed.type;
  329.     } catch (ex) { }
  330.  
  331.     return Ci.nsIFeed.TYPE_FEED;
  332.   },
  333.  
  334.   /**
  335.    * Maps a feed type to a maybe-feed mimetype.
  336.    */
  337.   _getMimeTypeForFeedType: function FW__getMimeTypeForFeedType() {
  338.     switch (this._getFeedType()) {
  339.       case Ci.nsIFeed.TYPE_VIDEO:
  340.         return TYPE_MAYBE_VIDEO_FEED;
  341.  
  342.       case Ci.nsIFeed.TYPE_AUDIO:
  343.         return TYPE_MAYBE_AUDIO_FEED;
  344.  
  345.       default:
  346.         return TYPE_MAYBE_FEED;
  347.     }
  348.   },
  349.  
  350.   /**
  351.    * Writes the feed title into the preview document.
  352.    * @param   container
  353.    *          The feed container
  354.    */
  355.   _setTitleText: function FW__setTitleText(container) {
  356.     if (container.title) {
  357.       var title = container.title.plainText();
  358.       this._setContentText(TITLE_ID, title);
  359.       this._contentSandbox.document = this._document;
  360.       this._contentSandbox.title = title;
  361.       var codeStr = "document.title = title;"
  362.       Cu.evalInSandbox(codeStr, this._contentSandbox);
  363.     }
  364.  
  365.     var feed = container.QueryInterface(Ci.nsIFeed);
  366.     if (feed && feed.subtitle)
  367.       this._setContentText(SUBTITLE_ID, container.subtitle.plainText());
  368.   },
  369.  
  370.   /**
  371.    * Writes the title image into the preview document if one is present.
  372.    * @param   container
  373.    *          The feed container
  374.    */
  375.   _setTitleImage: function FW__setTitleImage(container) {
  376.     try {
  377.       var parts = container.image;
  378.       
  379.       // Set up the title image (supplied by the feed)
  380.       var feedTitleImage = this._document.getElementById("feedTitleImage");
  381.       this._safeSetURIAttribute(feedTitleImage, "src", 
  382.                                 parts.getPropertyAsAString("url"));
  383.  
  384.       // Set up the title image link
  385.       var feedTitleLink = this._document.getElementById("feedTitleLink");
  386.  
  387.       var titleText = this._getFormattedString("linkTitleTextFormat", 
  388.                                                [parts.getPropertyAsAString("title")]);
  389.       this._contentSandbox.feedTitleLink = feedTitleLink;
  390.       this._contentSandbox.titleText = titleText;
  391.       this._contentSandbox.feedTitleText = this._document.getElementById("feedTitleText");
  392.       this._contentSandbox.titleImageWidth = parseInt(parts.getPropertyAsAString("width")) + 15;
  393.  
  394.       // Fix the margin on the main title, so that the image doesn't run over
  395.       // the underline
  396.       var codeStr = "feedTitleLink.setAttribute('title', titleText); " +
  397.                     "feedTitleText.style.marginRight = titleImageWidth + 'px';";
  398.       Cu.evalInSandbox(codeStr, this._contentSandbox);
  399.       this._contentSandbox.feedTitleLink = null;
  400.       this._contentSandbox.titleText = null;
  401.       this._contentSandbox.feedTitleText = null;
  402.       this._contentSandbox.titleImageWidth = null;
  403.  
  404.       this._safeSetURIAttribute(feedTitleLink, "href", 
  405.                                 parts.getPropertyAsAString("link"));
  406.     }
  407.     catch (e) {
  408.       LOG("Failed to set Title Image (this is benign): " + e);
  409.     }
  410.   },
  411.  
  412.   /**
  413.    * Writes all entries contained in the feed.
  414.    * @param   container
  415.    *          The container of entries in the feed
  416.    */
  417.   _writeFeedContent: function FW__writeFeedContent(container) {
  418.     // Build the actual feed content
  419.     var feed = container.QueryInterface(Ci.nsIFeed);
  420.     if (feed.items.length == 0)
  421.       return;
  422.  
  423.     this._contentSandbox.feedContent =
  424.       this._document.getElementById("feedContent");
  425.  
  426.     for (var i = 0; i < feed.items.length; ++i) {
  427.       var entry = feed.items.queryElementAt(i, Ci.nsIFeedEntry);
  428.       entry.QueryInterface(Ci.nsIFeedContainer);
  429.  
  430.       var entryContainer = this._document.createElementNS(HTML_NS, "div");
  431.       entryContainer.className = "entry";
  432.  
  433.       // If the entry has a title, make it a link
  434.       if (entry.title) {
  435.         var a = this._document.createElementNS(HTML_NS, "a");
  436.         a.appendChild(this._document.createTextNode(entry.title.plainText()));
  437.  
  438.         // Entries are not required to have links, so entry.link can be null.
  439.         if (entry.link)
  440.           this._safeSetURIAttribute(a, "href", entry.link.spec);
  441.  
  442.         var title = this._document.createElementNS(HTML_NS, "h3");
  443.         title.appendChild(a);
  444.  
  445.         var lastUpdated = this._parseDate(entry.updated);
  446.         if (lastUpdated) {
  447.           var dateDiv = this._document.createElementNS(HTML_NS, "div");
  448.           dateDiv.className = "lastUpdated";
  449.           dateDiv.textContent = lastUpdated;
  450.           title.appendChild(dateDiv);
  451.         }
  452.  
  453.         entryContainer.appendChild(title);
  454.       }
  455.  
  456.       var body = this._document.createElementNS(HTML_NS, "div");
  457.       var summary = entry.summary || entry.content;
  458.       var docFragment = null;
  459.       if (summary) {
  460.         if (summary.base)
  461.           body.setAttributeNS(XML_NS, "base", summary.base.spec);
  462.         else
  463.           LOG("no base?");
  464.         docFragment = summary.createDocumentFragment(body);
  465.         if (docFragment)
  466.           body.appendChild(docFragment);
  467.  
  468.         // If the entry doesn't have a title, append a # permalink
  469.         // See http://scripting.com/rss.xml for an example
  470.         if (!entry.title && entry.link) {
  471.           var a = this._document.createElementNS(HTML_NS, "a");
  472.           a.appendChild(this._document.createTextNode("#"));
  473.           this._safeSetURIAttribute(a, "href", entry.link.spec);
  474.           body.appendChild(this._document.createTextNode(" "));
  475.           body.appendChild(a);
  476.         }
  477.  
  478.       }
  479.       body.className = "feedEntryContent";
  480.       entryContainer.appendChild(body);
  481.  
  482.       if (entry.enclosures && entry.enclosures.length > 0) {
  483.         var enclosuresDiv = this._buildEnclosureDiv(entry);
  484.         entryContainer.appendChild(enclosuresDiv);
  485.       }
  486.  
  487.       this._contentSandbox.entryContainer = entryContainer;
  488.       this._contentSandbox.clearDiv =
  489.         this._document.createElementNS(HTML_NS, "div");
  490.       this._contentSandbox.clearDiv.style.clear = "both";
  491.       
  492.       var codeStr = "feedContent.appendChild(entryContainer); " +
  493.                      "feedContent.appendChild(clearDiv);"
  494.       Cu.evalInSandbox(codeStr, this._contentSandbox);
  495.     }
  496.  
  497.     this._contentSandbox.feedContent = null;
  498.     this._contentSandbox.entryContainer = null;
  499.     this._contentSandbox.clearDiv = null;
  500.   },
  501.  
  502.   /**
  503.    * Takes a url to a media item and returns the best name it can come up with.
  504.    * Frequently this is the filename portion (e.g. passing in 
  505.    * http://example.com/foo.mpeg would return "foo.mpeg"), but in more complex
  506.    * cases, this will return the entire url (e.g. passing in
  507.    * http://example.com/somedirectory/ would return 
  508.    * http://example.com/somedirectory/).
  509.    * @param aURL
  510.    *        The URL string from which to create a display name
  511.    * @returns a string
  512.    */
  513.   _getURLDisplayName: function FW__getURLDisplayName(aURL) {
  514.     var url = makeURI(aURL);
  515.     url.QueryInterface(Ci.nsIURL);
  516.     if (url == null || url.fileName.length == 0)
  517.       return aURL;
  518.  
  519.     return decodeURI(url.fileName);
  520.   },
  521.  
  522.   /**
  523.    * Takes a FeedEntry with enclosures, generates the HTML code to represent
  524.    * them, and returns that.
  525.    * @param   entry
  526.    *          FeedEntry with enclosures
  527.    * @returns element
  528.    */
  529.   _buildEnclosureDiv: function FW__buildEnclosureDiv(entry) {
  530.     var enclosuresDiv = this._document.createElementNS(HTML_NS, "div");
  531.     enclosuresDiv.className = "enclosures";
  532.  
  533.     enclosuresDiv.appendChild(this._document.createTextNode(this._getString("mediaLabel")));
  534.  
  535.     var roundme = function(n) {
  536.       return (Math.round(n * 100) / 100).toLocaleString();
  537.     }
  538.  
  539.     for (var i_enc = 0; i_enc < entry.enclosures.length; ++i_enc) {
  540.       var enc = entry.enclosures.queryElementAt(i_enc, Ci.nsIWritablePropertyBag2);
  541.  
  542.       if (!(enc.hasKey("url"))) 
  543.         continue;
  544.  
  545.       var enclosureDiv = this._document.createElementNS(HTML_NS, "div");
  546.       enclosureDiv.setAttribute("class", "enclosure");
  547.  
  548.       var mozicon = "moz-icon://.txt?size=16";
  549.       var type_text = null;
  550.       var size_text = null;
  551.  
  552.       if (enc.hasKey("type")) {
  553.         type_text = enc.get("type");
  554.         try {
  555.           var handlerInfoWrapper = this._mimeSvc.getFromTypeAndExtension(enc.get("type"), null);
  556.  
  557.           if (handlerInfoWrapper)
  558.             type_text = handlerInfoWrapper.description;
  559.  
  560.           if  (type_text && type_text.length > 0)
  561.             mozicon = "moz-icon://goat?size=16&contentType=" + enc.get("type");
  562.  
  563.         } catch (ex) { }
  564.  
  565.       }
  566.  
  567.       if (enc.hasKey("length") && /^[0-9]+$/.test(enc.get("length"))) {
  568.         var enc_size = convertByteUnits(parseInt(enc.get("length")));
  569.  
  570.         var size_text = this._getFormattedString("enclosureSizeText", 
  571.                              [enc_size[0], this._getString(enc_size[1])]);
  572.       }
  573.  
  574.       var iconimg = this._document.createElementNS(HTML_NS, "img");
  575.       iconimg.setAttribute("src", mozicon);
  576.       iconimg.setAttribute("class", "type-icon");
  577.       enclosureDiv.appendChild(iconimg);
  578.  
  579.       enclosureDiv.appendChild(this._document.createTextNode( " " ));
  580.  
  581.       var enc_href = this._document.createElementNS(HTML_NS, "a");
  582.       enc_href.appendChild(this._document.createTextNode(this._getURLDisplayName(enc.get("url"))));
  583.       this._safeSetURIAttribute(enc_href, "href", enc.get("url"));
  584.       enclosureDiv.appendChild(enc_href);
  585.  
  586.       if (type_text && size_text)
  587.         enclosureDiv.appendChild(this._document.createTextNode( " (" + type_text + ", " + size_text + ")"));
  588.  
  589.       else if (type_text) 
  590.         enclosureDiv.appendChild(this._document.createTextNode( " (" + type_text + ")"))
  591.  
  592.       else if (size_text)
  593.         enclosureDiv.appendChild(this._document.createTextNode( " (" + size_text + ")"))
  594.  
  595.       enclosuresDiv.appendChild(enclosureDiv);
  596.     }
  597.  
  598.     return enclosuresDiv;
  599.   },
  600.  
  601.   /**
  602.    * Gets a valid nsIFeedContainer object from the parsed nsIFeedResult.
  603.    * Displays error information if there was one.
  604.    * @param   result
  605.    *          The parsed feed result
  606.    * @returns A valid nsIFeedContainer object containing the contents of
  607.    *          the feed.
  608.    */
  609.   _getContainer: function FW__getContainer(result) {
  610.     var feedService = 
  611.         Cc["@mozilla.org/browser/feeds/result-service;1"].
  612.         getService(Ci.nsIFeedResultService);
  613.  
  614.     try {
  615.       var result = 
  616.         feedService.getFeedResult(this._getOriginalURI(this._window));
  617.     }
  618.     catch (e) {
  619.       LOG("Subscribe Preview: feed not available?!");
  620.     }
  621.     
  622.     if (result.bozo) {
  623.       LOG("Subscribe Preview: feed result is bozo?!");
  624.     }
  625.  
  626.     try {
  627.       var container = result.doc;
  628.     }
  629.     catch (e) {
  630.       LOG("Subscribe Preview: no result.doc? Why didn't the original reload?");
  631.       return null;
  632.     }
  633.     return container;
  634.   },
  635.  
  636.   /**
  637.    * Get the human-readable display name of a file. This could be the 
  638.    * application name.
  639.    * @param   file
  640.    *          A nsIFile to look up the name of
  641.    * @returns The display name of the application represented by the file.
  642.    */
  643.   _getFileDisplayName: function FW__getFileDisplayName(file) {
  644. //@line 702 "/build/buildd/firefox-3.5-3.5.5+nobinonly/build-tree/mozilla/browser/components/feeds/src/FeedWriter.js"
  645.     var ios = 
  646.         Cc["@mozilla.org/network/io-service;1"].
  647.         getService(Ci.nsIIOService);
  648.     var url = ios.newFileURI(file).QueryInterface(Ci.nsIURL);
  649.     return url.fileName;
  650.   },
  651.  
  652.   /**
  653.    * Get moz-icon url for a file
  654.    * @param   file
  655.    *          A nsIFile object for which the moz-icon:// is returned
  656.    * @returns moz-icon url of the given file as a string
  657.    */
  658.   _getFileIconURL: function FW__getFileIconURL(file) {
  659.     var ios = Cc["@mozilla.org/network/io-service;1"].
  660.               getService(Components.interfaces.nsIIOService);
  661.     var fph = ios.getProtocolHandler("file")
  662.                  .QueryInterface(Ci.nsIFileProtocolHandler);
  663.     var urlSpec = fph.getURLSpecFromFile(file);
  664.     return "moz-icon://" + urlSpec + "?size=16";
  665.   },
  666.  
  667.   /**
  668.    * Helper method to set the selected application and system default
  669.    * reader menuitems details from a file object
  670.    *   @param aMenuItem
  671.    *          The menuitem on which the attributes should be set
  672.    *   @param aFile
  673.    *          The menuitem's associated file
  674.    */
  675.   _initMenuItemWithFile: function(aMenuItem, aFile) {
  676.     this._contentSandbox.menuitem = aMenuItem;
  677.     this._contentSandbox.label = this._getFileDisplayName(aFile);
  678.     this._contentSandbox.image = this._getFileIconURL(aFile);
  679.     var codeStr = "menuitem.setAttribute('label', label); " +
  680.                   "menuitem.setAttribute('image', image);"
  681.     Cu.evalInSandbox(codeStr, this._contentSandbox);
  682.   },
  683.  
  684.   /**
  685.    * Displays a prompt from which the user may choose a (client) feed reader.
  686.    * @return - true if a feed reader was selected, false otherwise.
  687.    */
  688.   _chooseClientApp: function FW__chooseClientApp() {
  689.     try {
  690.       var fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
  691.       fp.init(this._window,
  692.               this._getString("chooseApplicationDialogTitle"),
  693.               Ci.nsIFilePicker.modeOpen);
  694.       fp.appendFilters(Ci.nsIFilePicker.filterApps);
  695.  
  696.       if (fp.show() == Ci.nsIFilePicker.returnOK) {
  697.         this._selectedApp = fp.file;
  698.         if (this._selectedApp) {
  699.           // XXXben - we need to compare this with the running instance executable
  700.           //          just don't know how to do that via script...
  701.           // XXXmano TBD: can probably add this to nsIShellService
  702. //@line 765 "/build/buildd/firefox-3.5-3.5.5+nobinonly/build-tree/mozilla/browser/components/feeds/src/FeedWriter.js"
  703.           if (fp.file.leafName != "firefox-bin") {
  704. //@line 768 "/build/buildd/firefox-3.5-3.5.5+nobinonly/build-tree/mozilla/browser/components/feeds/src/FeedWriter.js"
  705.             this._initMenuItemWithFile(this._contentSandbox.selectedAppMenuItem,
  706.                                        this._selectedApp);
  707.  
  708.             // Show and select the selected application menuitem
  709.             var codeStr = "selectedAppMenuItem.hidden = false;" +
  710.                           "selectedAppMenuItem.doCommand();"
  711.             Cu.evalInSandbox(codeStr, this._contentSandbox);
  712.             return true;
  713.           }
  714.         }
  715.       }
  716.     }
  717.     catch(ex) { }
  718.  
  719.     return false;
  720.   },
  721.  
  722.   _setAlwaysUseCheckedState: function FW__setAlwaysUseCheckedState(feedType) {
  723.     var checkbox = this._document.getElementById("alwaysUse");
  724.     if (checkbox) {
  725.       var alwaysUse = false;
  726.       try {
  727.         var prefs = Cc["@mozilla.org/preferences-service;1"].
  728.                     getService(Ci.nsIPrefBranch);
  729.         if (prefs.getCharPref(getPrefActionForType(feedType)) != "ask")
  730.           alwaysUse = true;
  731.       }
  732.       catch(ex) { }
  733.       this._setCheckboxCheckedState(checkbox, alwaysUse);
  734.     }
  735.   },
  736.  
  737.   _setSubscribeUsingLabel: function FW__setSubscribeUsingLabel() {
  738.     var stringLabel = "subscribeFeedUsing";
  739.     switch (this._getFeedType()) {
  740.       case Ci.nsIFeed.TYPE_VIDEO:
  741.         stringLabel = "subscribeVideoPodcastUsing";
  742.         break;
  743.  
  744.       case Ci.nsIFeed.TYPE_AUDIO:
  745.         stringLabel = "subscribeAudioPodcastUsing";
  746.         break;
  747.     }
  748.  
  749.     this._contentSandbox.subscribeUsing =
  750.       this._document.getElementById("subscribeUsingDescription");
  751.     this._contentSandbox.label = this._getString(stringLabel);
  752.     var codeStr = "subscribeUsing.setAttribute('value', label);"
  753.     Cu.evalInSandbox(codeStr, this._contentSandbox);
  754.   },
  755.  
  756.   _setAlwaysUseLabel: function FW__setAlwaysUseLabel() {
  757.     var checkbox = this._document.getElementById("alwaysUse");
  758.     if (checkbox) {
  759.       var handlersMenuList = this._document.getElementById("handlersMenuList");
  760.       if (handlersMenuList) {
  761.         var handlerName = this._getSelectedItemFromMenulist(handlersMenuList)
  762.                               .getAttribute("label");
  763.         var stringLabel = "alwaysUseForFeeds";
  764.         switch (this._getFeedType()) {
  765.           case Ci.nsIFeed.TYPE_VIDEO:
  766.             stringLabel = "alwaysUseForVideoPodcasts";
  767.             break;
  768.  
  769.           case Ci.nsIFeed.TYPE_AUDIO:
  770.             stringLabel = "alwaysUseForAudioPodcasts";
  771.             break;
  772.         }
  773.  
  774.         this._contentSandbox.checkbox = checkbox;
  775.         this._contentSandbox.label = this._getFormattedString(stringLabel, [handlerName]);
  776.         
  777.         var codeStr = "checkbox.setAttribute('label', label);";
  778.         Cu.evalInSandbox(codeStr, this._contentSandbox);
  779.       }
  780.     }
  781.   },
  782.  
  783.   // nsIDomEventListener
  784.   handleEvent: function(event) {
  785.     // see comments in init()
  786.     event = new XPCNativeWrapper(event);
  787.     if (event.target.ownerDocument != this._document) {
  788.       LOG("FeedWriter.handleEvent: Someone passed the feed writer as a listener to the events of another document!");
  789.       return;
  790.     }
  791.  
  792.     if (event.type == "command") {
  793.       switch (event.target.id) {
  794.         case "subscribeButton":
  795.           this.subscribe();
  796.           break;
  797.         case "chooseApplicationMenuItem":
  798.           /* Bug 351263: Make sure to not steal focus if the "Choose
  799.            * Application" item is being selected with the keyboard. We do this
  800.            * by ignoring command events while the dropdown is closed (user
  801.            * arrowing through the combobox), but handling them while the
  802.            * combobox dropdown is open (user pressed enter when an item was
  803.            * selected). If we don't show the filepicker here, it will be shown
  804.            * when clicking "Subscribe Now".
  805.            */
  806.           var popupbox = this._document.getElementById("handlersMenuList")
  807.                              .firstChild.boxObject;
  808.           popupbox.QueryInterface(Components.interfaces.nsIPopupBoxObject);
  809.           if (popupbox.popupState == "hiding" && !this._chooseClientApp()) {
  810.             // Select the (per-prefs) selected handler if no application was
  811.             // selected
  812.             this._setSelectedHandler(this._getFeedType());
  813.           }
  814.           break;
  815.         default:
  816.           this._setAlwaysUseLabel();
  817.       }
  818.     }
  819.   },
  820.  
  821.   _setSelectedHandler: function FW__setSelectedHandler(feedType) {
  822.     var prefs =   
  823.         Cc["@mozilla.org/preferences-service;1"].
  824.         getService(Ci.nsIPrefBranch);
  825.  
  826.     var handler = "bookmarks";
  827.     try {
  828.       handler = prefs.getCharPref(getPrefReaderForType(feedType));
  829.     }
  830.     catch (ex) { }
  831.  
  832.     switch (handler) {
  833.       case "web": {
  834.         var handlersMenuList = this._document.getElementById("handlersMenuList");
  835.         if (handlersMenuList) {
  836.           var url = prefs.getComplexValue(getPrefWebForType(feedType), Ci.nsISupportsString).data;
  837.           var handlers =
  838.             handlersMenuList.getElementsByAttribute("webhandlerurl", url);
  839.           if (handlers.length == 0) {
  840.             LOG("FeedWriter._setSelectedHandler: selected web handler isn't in the menulist")
  841.             return;
  842.           }
  843.  
  844.           this._safeDoCommand(handlers[0]);
  845.         }
  846.         break;
  847.       }
  848.       case "client": {
  849.         try {
  850.           this._selectedApp =
  851.             prefs.getComplexValue(getPrefAppForType(feedType), Ci.nsILocalFile);
  852.         }
  853.         catch(ex) {
  854.           this._selectedApp = null;
  855.         }
  856.  
  857.         if (this._selectedApp) {
  858.           this._initMenuItemWithFile(this._contentSandbox.selectedAppMenuItem,
  859.                                      this._selectedApp);
  860.           var codeStr = "selectedAppMenuItem.hidden = false; " +
  861.                         "selectedAppMenuItem.doCommand(); ";
  862.  
  863.           // Only show the default reader menuitem if the default reader
  864.           // isn't the selected application
  865.           if (this._defaultSystemReader) {
  866.             var shouldHide =
  867.               this._defaultSystemReader.path == this._selectedApp.path;
  868.             codeStr += "defaultHandlerMenuItem.hidden = " + shouldHide + ";"
  869.           }
  870.           Cu.evalInSandbox(codeStr, this._contentSandbox);
  871.           break;
  872.         }
  873.       }
  874.       case "bookmarks":
  875.       default: {
  876.         var liveBookmarksMenuItem = this._document.getElementById("liveBookmarksMenuItem");
  877.         if (liveBookmarksMenuItem)
  878.           this._safeDoCommand(liveBookmarksMenuItem);
  879.       } 
  880.     }
  881.   },
  882.  
  883.   _initSubscriptionUI: function FW__initSubscriptionUI() {
  884.     var handlersMenuPopup = this._document.getElementById("handlersMenuPopup");
  885.     if (!handlersMenuPopup)
  886.       return;
  887.  
  888.     var feedType = this._getFeedType();
  889.     var codeStr;
  890.  
  891.     // change the background
  892.     var header = this._document.getElementById("feedHeader");
  893.     this._contentSandbox.header = header;
  894.     switch (feedType) {
  895.       case Ci.nsIFeed.TYPE_VIDEO:
  896.         codeStr = "header.className = 'videoPodcastBackground'; ";
  897.         break;
  898.  
  899.       case Ci.nsIFeed.TYPE_AUDIO:
  900.         codeStr = "header.className = 'audioPodcastBackground'; ";
  901.         break;
  902.  
  903.       default:
  904.         codeStr = "header.className = 'feedBackground'; ";
  905.     }
  906.  
  907.  
  908.     // Last-selected application
  909.     var menuItem = this._document.createElementNS(XUL_NS, "menuitem");
  910.     menuItem.id = "selectedAppMenuItem";
  911.     menuItem.className = "menuitem-iconic";
  912.     menuItem.setAttribute("handlerType", "client");
  913.     try {
  914.       var prefs = Cc["@mozilla.org/preferences-service;1"].
  915.                   getService(Ci.nsIPrefBranch);
  916.       this._selectedApp = prefs.getComplexValue(getPrefAppForType(feedType),
  917.                                                 Ci.nsILocalFile);
  918.  
  919.       if (this._selectedApp.exists())
  920.         this._initMenuItemWithFile(menuItem, this._selectedApp);
  921.       else {
  922.         // Hide the menuitem if the last selected application doesn't exist
  923.         menuItem.setAttribute("hidden", true);
  924.       }
  925.     }
  926.     catch(ex) {
  927.       // Hide the menuitem until an application is selected
  928.       menuItem.setAttribute("hidden", true);
  929.     }
  930.     this._contentSandbox.handlersMenuPopup = handlersMenuPopup;
  931.     this._contentSandbox.selectedAppMenuItem = menuItem;
  932.     
  933.     codeStr += "handlersMenuPopup.appendChild(selectedAppMenuItem); ";
  934.  
  935.     // List the default feed reader
  936.     try {
  937.       this._defaultSystemReader = Cc["@mozilla.org/browser/shell-service;1"].
  938.                                   getService(Ci.nsIShellService).
  939.                                   defaultFeedReader;
  940.       menuItem = this._document.createElementNS(XUL_NS, "menuitem");
  941.       menuItem.id = "defaultHandlerMenuItem";
  942.       menuItem.className = "menuitem-iconic";
  943.       menuItem.setAttribute("handlerType", "client");
  944.  
  945.       this._initMenuItemWithFile(menuItem, this._defaultSystemReader);
  946.  
  947.       // Hide the default reader item if it points to the same application
  948.       // as the last-selected application
  949.       if (this._selectedApp &&
  950.           this._selectedApp.path == this._defaultSystemReader.path)
  951.         menuItem.hidden = true;
  952.     }
  953.     catch(ex) { menuItem = null; /* no default reader */ }
  954.  
  955.     if (menuItem) {
  956.       this._contentSandbox.defaultHandlerMenuItem = menuItem;
  957.       codeStr += "handlersMenuPopup.appendChild(defaultHandlerMenuItem); ";
  958.     }
  959.  
  960.     // "Choose Application..." menuitem
  961.     menuItem = this._document.createElementNS(XUL_NS, "menuitem");
  962.     menuItem.id = "chooseApplicationMenuItem";
  963.     menuItem.className = "menuitem-iconic";
  964.     menuItem.setAttribute("label", this._getString("chooseApplicationMenuItem"));
  965.  
  966.     this._contentSandbox.chooseAppMenuItem = menuItem;
  967.     codeStr += "handlersMenuPopup.appendChild(chooseAppMenuItem); ";
  968.  
  969.     // separator
  970.     this._contentSandbox.chooseAppSep =
  971.       this._document.createElementNS(XUL_NS, "menuseparator")
  972.     codeStr += "handlersMenuPopup.appendChild(chooseAppSep); ";
  973.  
  974.     Cu.evalInSandbox(codeStr, this._contentSandbox);
  975.  
  976.     var historySvc = Cc["@mozilla.org/browser/nav-history-service;1"].
  977.                      getService(Ci.nsINavHistoryService);
  978.     historySvc.addObserver(this, false);
  979.  
  980.     // List of web handlers
  981.     var wccr = Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].
  982.                getService(Ci.nsIWebContentConverterService);
  983.     var handlers = wccr.getContentHandlers(this._getMimeTypeForFeedType(feedType), {});
  984.     if (handlers.length != 0) {
  985.       for (var i = 0; i < handlers.length; ++i) {
  986.         menuItem = this._document.createElementNS(XUL_NS, "menuitem");
  987.         menuItem.className = "menuitem-iconic";
  988.         menuItem.setAttribute("label", handlers[i].name);
  989.         menuItem.setAttribute("handlerType", "web");
  990.         menuItem.setAttribute("webhandlerurl", handlers[i].uri);
  991.         this._contentSandbox.menuItem = menuItem;
  992.         codeStr = "handlersMenuPopup.appendChild(menuItem);";
  993.         Cu.evalInSandbox(codeStr, this._contentSandbox);
  994.  
  995.         // For privacy reasons we cannot set the image attribute directly
  996.         // to the icon url, see Bug 358878
  997.         var uri = makeURI(handlers[i].uri);
  998.         if (!this._setFaviconForWebReader(uri, menuItem)) {
  999.           if (uri && /^https?/.test(uri.scheme)) {
  1000.             var iconURL = makeURI(uri.prePath + "/favicon.ico");
  1001.             this._faviconService.setAndLoadFaviconForPage(uri, iconURL, true);
  1002.           }
  1003.         }
  1004.       }
  1005.       this._contentSandbox.menuItem = null;
  1006.     }
  1007.  
  1008.     this._setSelectedHandler(feedType);
  1009.  
  1010.     // "Subscribe using..."
  1011.     this._setSubscribeUsingLabel();
  1012.  
  1013.     // "Always use..." checkbox initial state
  1014.     this._setAlwaysUseCheckedState(feedType);
  1015.     this._setAlwaysUseLabel();
  1016.  
  1017.     // We update the "Always use.." checkbox label whenever the selected item
  1018.     // in the list is changed
  1019.     handlersMenuPopup.addEventListener("command", this, false);
  1020.  
  1021.     // Set up the "Subscribe Now" button
  1022.     this._document
  1023.         .getElementById("subscribeButton")
  1024.         .addEventListener("command", this, false);
  1025.  
  1026.     // first-run ui
  1027.     var showFirstRunUI = true;
  1028.     try {
  1029.       showFirstRunUI = prefs.getBoolPref(PREF_SHOW_FIRST_RUN_UI);
  1030.     }
  1031.     catch (ex) { }
  1032.     if (showFirstRunUI) {
  1033.       var textfeedinfo1, textfeedinfo2;
  1034.       switch (feedType) {
  1035.         case Ci.nsIFeed.TYPE_VIDEO:
  1036.           textfeedinfo1 = "feedSubscriptionVideoPodcast1";
  1037.           textfeedinfo2 = "feedSubscriptionVideoPodcast2";
  1038.           break;
  1039.         case Ci.nsIFeed.TYPE_AUDIO:
  1040.           textfeedinfo1 = "feedSubscriptionAudioPodcast1";
  1041.           textfeedinfo2 = "feedSubscriptionAudioPodcast2";
  1042.           break;
  1043.         default:
  1044.           textfeedinfo1 = "feedSubscriptionFeed1";
  1045.           textfeedinfo2 = "feedSubscriptionFeed2";
  1046.       }
  1047.  
  1048.       this._contentSandbox.feedinfo1 =
  1049.         this._document.getElementById("feedSubscriptionInfo1");
  1050.       this._contentSandbox.feedinfo1Str = this._getString(textfeedinfo1);
  1051.       this._contentSandbox.feedinfo2 =
  1052.         this._document.getElementById("feedSubscriptionInfo2");
  1053.       this._contentSandbox.feedinfo2Str = this._getString(textfeedinfo2);
  1054.       this._contentSandbox.header = header;
  1055.       codeStr = "feedinfo1.textContent = feedinfo1Str; " +
  1056.                 "feedinfo2.textContent = feedinfo2Str; " +
  1057.                 "header.setAttribute('firstrun', 'true');"
  1058.       Cu.evalInSandbox(codeStr, this._contentSandbox);
  1059.       prefs.setBoolPref(PREF_SHOW_FIRST_RUN_UI, false);
  1060.     }
  1061.   },
  1062.  
  1063.   /**
  1064.    * Returns the original URI object of the feed and ensures that this
  1065.    * component is only ever invoked from the preview document.  
  1066.    * @param aWindow 
  1067.    *        The window of the document invoking the BrowserFeedWriter
  1068.    */
  1069.   _getOriginalURI: function FW__getOriginalURI(aWindow) {
  1070.     var chan = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).
  1071.                getInterface(Ci.nsIWebNavigation).
  1072.                QueryInterface(Ci.nsIDocShell).currentDocumentChannel;
  1073.  
  1074.     var uri = makeURI(SUBSCRIBE_PAGE_URI);
  1075.     var resolvedURI = Cc["@mozilla.org/chrome/chrome-registry;1"].
  1076.                       getService(Ci.nsIChromeRegistry).
  1077.                       convertChromeURL(uri);
  1078.  
  1079.     if (resolvedURI.equals(chan.URI))
  1080.       return chan.originalURI;
  1081.  
  1082.     return null;
  1083.   },
  1084.  
  1085.   _window: null,
  1086.   _document: null,
  1087.   _feedURI: null,
  1088.   _feedPrincipal: null,
  1089.  
  1090.   // nsIFeedWriter
  1091.   init: function FW_init(aWindow) {
  1092.     // Explicitly wrap |window| in an XPCNativeWrapper to make sure
  1093.     // it's a real native object! This will throw an exception if we
  1094.     // get a non-native object.
  1095.     var window = new XPCNativeWrapper(aWindow);
  1096.     this._feedURI = this._getOriginalURI(window);
  1097.     if (!this._feedURI)
  1098.       return;
  1099.  
  1100.     this._window = window;
  1101.     this._document = window.document;
  1102.  
  1103.     var secman = Cc["@mozilla.org/scriptsecuritymanager;1"].
  1104.                  getService(Ci.nsIScriptSecurityManager);
  1105.     this._feedPrincipal = secman.getCodebasePrincipal(this._feedURI);
  1106.  
  1107.     LOG("Subscribe Preview: feed uri = " + this._window.location.href);
  1108.  
  1109.     // Set up the subscription UI
  1110.     this._initSubscriptionUI();
  1111.     var prefs = Cc["@mozilla.org/preferences-service;1"].
  1112.                 getService(Ci.nsIPrefBranch2);
  1113.     prefs.addObserver(PREF_SELECTED_ACTION, this, false);
  1114.     prefs.addObserver(PREF_SELECTED_READER, this, false);
  1115.     prefs.addObserver(PREF_SELECTED_WEB, this, false);
  1116.     prefs.addObserver(PREF_SELECTED_APP, this, false);
  1117.     prefs.addObserver(PREF_VIDEO_SELECTED_ACTION, this, false);
  1118.     prefs.addObserver(PREF_VIDEO_SELECTED_READER, this, false);
  1119.     prefs.addObserver(PREF_VIDEO_SELECTED_WEB, this, false);
  1120.     prefs.addObserver(PREF_VIDEO_SELECTED_APP, this, false);
  1121.  
  1122.     prefs.addObserver(PREF_AUDIO_SELECTED_ACTION, this, false);
  1123.     prefs.addObserver(PREF_AUDIO_SELECTED_READER, this, false);
  1124.     prefs.addObserver(PREF_AUDIO_SELECTED_WEB, this, false);
  1125.     prefs.addObserver(PREF_AUDIO_SELECTED_APP, this, false);
  1126.   },
  1127.  
  1128.   writeContent: function FW_writeContent() {
  1129.     if (!this._window)
  1130.       return;
  1131.  
  1132.     try {
  1133.       // Set up the feed content
  1134.       var container = this._getContainer();
  1135.       if (!container)
  1136.         return;
  1137.  
  1138.       this._setTitleText(container);
  1139.       this._setTitleImage(container);
  1140.       this._writeFeedContent(container);
  1141.     }
  1142.     finally {
  1143.       this._removeFeedFromCache();
  1144.     }
  1145.   },
  1146.  
  1147.   close: function FW_close() {
  1148.     this._document
  1149.         .getElementById("handlersMenuPopup")
  1150.         .removeEventListener("command", this, false);
  1151.     this._document
  1152.         .getElementById("subscribeButton")
  1153.         .removeEventListener("command", this, false);
  1154.     this._document = null;
  1155.     this._window = null;
  1156.     var prefs = Cc["@mozilla.org/preferences-service;1"].
  1157.                 getService(Ci.nsIPrefBranch2);
  1158.     prefs.removeObserver(PREF_SELECTED_ACTION, this);
  1159.     prefs.removeObserver(PREF_SELECTED_READER, this);
  1160.     prefs.removeObserver(PREF_SELECTED_WEB, this);
  1161.     prefs.removeObserver(PREF_SELECTED_APP, this);
  1162.     prefs.removeObserver(PREF_VIDEO_SELECTED_ACTION, this);
  1163.     prefs.removeObserver(PREF_VIDEO_SELECTED_READER, this);
  1164.     prefs.removeObserver(PREF_VIDEO_SELECTED_WEB, this);
  1165.     prefs.removeObserver(PREF_VIDEO_SELECTED_APP, this);
  1166.  
  1167.     prefs.removeObserver(PREF_AUDIO_SELECTED_ACTION, this);
  1168.     prefs.removeObserver(PREF_AUDIO_SELECTED_READER, this);
  1169.     prefs.removeObserver(PREF_AUDIO_SELECTED_WEB, this);
  1170.     prefs.removeObserver(PREF_AUDIO_SELECTED_APP, this);
  1171.  
  1172.     this._removeFeedFromCache();
  1173.     this.__faviconService = null;
  1174.     this.__bundle = null;
  1175.     this._feedURI = null;
  1176.     this.__contentSandbox = null;
  1177.  
  1178.     var historySvc = Cc["@mozilla.org/browser/nav-history-service;1"].
  1179.                      getService(Ci.nsINavHistoryService);
  1180.     historySvc.removeObserver(this);
  1181.   },
  1182.  
  1183.   _removeFeedFromCache: function FW__removeFeedFromCache() {
  1184.     if (this._feedURI) {
  1185.       var feedService = Cc["@mozilla.org/browser/feeds/result-service;1"].
  1186.                         getService(Ci.nsIFeedResultService);
  1187.       feedService.removeFeedResult(this._feedURI);
  1188.       this._feedURI = null;
  1189.     }
  1190.   },
  1191.  
  1192.   subscribe: function FW_subscribe() {
  1193.     var feedType = this._getFeedType();
  1194.  
  1195.     // Subscribe to the feed using the selected handler and save prefs
  1196.     var prefs = Cc["@mozilla.org/preferences-service;1"].
  1197.                 getService(Ci.nsIPrefBranch);
  1198.     var defaultHandler = "reader";
  1199.     var useAsDefault = this._document.getElementById("alwaysUse")
  1200.                                      .getAttribute("checked");
  1201.  
  1202.     var handlersMenuList = this._document.getElementById("handlersMenuList");
  1203.     var selectedItem = this._getSelectedItemFromMenulist(handlersMenuList);
  1204.  
  1205.     // Show the file picker before subscribing if the
  1206.     // choose application menuitem was choosen using the keyboard
  1207.     if (selectedItem.id == "chooseApplicationMenuItem") {
  1208.       if (!this._chooseClientApp())
  1209.         return;
  1210.       
  1211.       selectedItem = this._getSelectedItemFromMenulist(handlersMenuList);
  1212.     }
  1213.  
  1214.     if (selectedItem.hasAttribute("webhandlerurl")) {
  1215.       var webURI = selectedItem.getAttribute("webhandlerurl");
  1216.       prefs.setCharPref(getPrefReaderForType(feedType), "web");
  1217.  
  1218.       var supportsString = Cc["@mozilla.org/supports-string;1"].
  1219.                            createInstance(Ci.nsISupportsString);
  1220.       supportsString.data = webURI;
  1221.       prefs.setComplexValue(getPrefWebForType(feedType), Ci.nsISupportsString,
  1222.                             supportsString);
  1223.  
  1224.       var wccr = Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].
  1225.                  getService(Ci.nsIWebContentConverterService);
  1226.       var handler = wccr.getWebContentHandlerByURI(this._getMimeTypeForFeedType(feedType), webURI);
  1227.       if (handler) {
  1228.         if (useAsDefault)
  1229.           wccr.setAutoHandler(this._getMimeTypeForFeedType(feedType), handler);
  1230.  
  1231.         this._window.location.href = handler.getHandlerURI(this._window.location.href);
  1232.       }
  1233.     }
  1234.     else {
  1235.       switch (selectedItem.id) {
  1236.         case "selectedAppMenuItem":
  1237.           prefs.setComplexValue(getPrefAppForType(feedType), Ci.nsILocalFile, 
  1238.                                 this._selectedApp);
  1239.           prefs.setCharPref(getPrefReaderForType(feedType), "client");
  1240.           break;
  1241.         case "defaultHandlerMenuItem":
  1242.           prefs.setComplexValue(getPrefAppForType(feedType), Ci.nsILocalFile, 
  1243.                                 this._defaultSystemReader);
  1244.           prefs.setCharPref(getPrefReaderForType(feedType), "client");
  1245.           break;
  1246.         case "liveBookmarksMenuItem":
  1247.           defaultHandler = "bookmarks";
  1248.           prefs.setCharPref(getPrefReaderForType(feedType), "bookmarks");
  1249.           break;
  1250.       }
  1251.       var feedService = Cc["@mozilla.org/browser/feeds/result-service;1"].
  1252.                         getService(Ci.nsIFeedResultService);
  1253.  
  1254.       // Pull the title and subtitle out of the document
  1255.       var feedTitle = this._document.getElementById(TITLE_ID).textContent;
  1256.       var feedSubtitle = this._document.getElementById(SUBTITLE_ID).textContent;
  1257.       feedService.addToClientReader(this._window.location.href, feedTitle, feedSubtitle, feedType);
  1258.     }
  1259.  
  1260.     // If "Always use..." is checked, we should set PREF_*SELECTED_ACTION
  1261.     // to either "reader" (If a web reader or if an application is selected),
  1262.     // or to "bookmarks" (if the live bookmarks option is selected).
  1263.     // Otherwise, we should set it to "ask"
  1264.     if (useAsDefault)
  1265.       prefs.setCharPref(getPrefActionForType(feedType), defaultHandler);
  1266.     else
  1267.       prefs.setCharPref(getPrefActionForType(feedType), "ask");
  1268.   },
  1269.  
  1270.   // nsIObserver
  1271.   observe: function FW_observe(subject, topic, data) {
  1272.     // see init()
  1273.     subject = new XPCNativeWrapper(subject);
  1274.     
  1275.     if (!this._window) {
  1276.       // this._window is null unless this.init was called with a trusted
  1277.       // window object.
  1278.       return;
  1279.     }
  1280.  
  1281.     var feedType = this._getFeedType();
  1282.  
  1283.     if (topic == "nsPref:changed") {
  1284.       switch (data) {
  1285.         case PREF_SELECTED_READER:
  1286.         case PREF_SELECTED_WEB:
  1287.         case PREF_SELECTED_APP:
  1288.         case PREF_VIDEO_SELECTED_READER:
  1289.         case PREF_VIDEO_SELECTED_WEB:
  1290.         case PREF_VIDEO_SELECTED_APP:
  1291.         case PREF_AUDIO_SELECTED_READER:
  1292.         case PREF_AUDIO_SELECTED_WEB:
  1293.         case PREF_AUDIO_SELECTED_APP:
  1294.           this._setSelectedHandler(feedType);
  1295.           break;
  1296.         case PREF_SELECTED_ACTION:
  1297.         case PREF_VIDEO_SELECTED_ACTION:
  1298.         case PREF_AUDIO_SELECTED_ACTION:
  1299.           this._setAlwaysUseCheckedState(feedType);
  1300.       }
  1301.     } 
  1302.   },
  1303.  
  1304.   /**
  1305.    * Sets the icon for the given web-reader item in the readers menu
  1306.    * if the favicon-service has the necessary icon stored.
  1307.    * @param aURI
  1308.    *        the reader URI.
  1309.    * @param aMenuItem
  1310.    *        the reader item in the readers menulist.
  1311.    * @return true if the icon was set, false otherwise.
  1312.    */
  1313.   _setFaviconForWebReader:
  1314.   function FW__setFaviconForWebReader(aURI, aMenuItem) {
  1315.     var faviconsSvc = this._faviconService;
  1316.     var faviconURI = null;
  1317.     try {
  1318.       faviconURI = faviconsSvc.getFaviconForPage(aURI);
  1319.     }
  1320.     catch(ex) { }
  1321.  
  1322.     if (faviconURI) {
  1323.       var dataURL = faviconsSvc.getFaviconDataAsDataURL(faviconURI);
  1324.       if (dataURL) {
  1325.         this._contentSandbox.menuItem = aMenuItem;
  1326.         this._contentSandbox.dataURL = dataURL;
  1327.         var codeStr = "menuItem.setAttribute('image', dataURL);";
  1328.         Cu.evalInSandbox(codeStr, this._contentSandbox);
  1329.         this._contentSandbox.menuItem = null;
  1330.         this._contentSandbox.dataURL = null;
  1331.  
  1332.         return true;
  1333.       }
  1334.     }
  1335.  
  1336.     return false;
  1337.   },
  1338.  
  1339.    // nsINavHistoryService
  1340.    onPageChanged: function FW_onPageChanged(aURI, aWhat, aValue) {
  1341.      // see init()
  1342.      aURI = new XPCNativeWrapper(aURI);
  1343.  
  1344.      if (aWhat == Ci.nsINavHistoryObserver.ATTRIBUTE_FAVICON) {
  1345.        // Go through the readers menu and look for the corresponding
  1346.        // reader menu-item for the page if any.
  1347.        var spec = aURI.spec;
  1348.        var handlersMenulist = this._document.getElementById("handlersMenuList");
  1349.        var possibleHandlers = handlersMenulist.firstChild.childNodes;
  1350.        for (var i=0; i < possibleHandlers.length ; i++) {
  1351.          if (possibleHandlers[i].getAttribute("webhandlerurl") == spec) {
  1352.            this._setFaviconForWebReader(aURI, possibleHandlers[i]);
  1353.            return;
  1354.          }
  1355.        }
  1356.      }
  1357.    },
  1358.  
  1359.    onBeginUpdateBatch: function() { },
  1360.    onEndUpdateBatch: function() { },
  1361.    onVisit: function() { },
  1362.    onTitleChanged: function() { },
  1363.    onDeleteURI: function() { },
  1364.    onClearHistory: function() { },
  1365.    onPageExpired: function() { },
  1366.  
  1367.   // nsIClassInfo
  1368.   getInterfaces: function FW_getInterfaces(countRef) {
  1369.     var interfaces = [Ci.nsIFeedWriter, Ci.nsIClassInfo, Ci.nsISupports];
  1370.     countRef.value = interfaces.length;
  1371.     return interfaces;
  1372.   },
  1373.   getHelperForLanguage: function FW_getHelperForLanguage(language) null,
  1374.   contractID: "@mozilla.org/browser/feeds/result-writer;1",
  1375.   classDescription: "Feed Writer",
  1376.   classID: Components.ID("{49bb6593-3aff-4eb3-a068-2712c28bd58e}"),
  1377.   implementationLanguage: Ci.nsIProgrammingLanguage.JAVASCRIPT,
  1378.   flags: Ci.nsIClassInfo.DOM_OBJECT,
  1379.   _xpcom_categories: [{ category: "JavaScript global constructor",
  1380.                         entry: "BrowserFeedWriter"}],
  1381.   QueryInterface: XPCOMUtils.generateQI([Ci.nsIFeedWriter, Ci.nsIClassInfo,
  1382.                                          Ci.nsIDOMEventListener, Ci.nsIObserver,
  1383.                                          Ci.nsINavHistoryObserver])
  1384. };
  1385.  
  1386. function NSGetModule(cm, file)
  1387.   XPCOMUtils.generateModule([FeedWriter]);
  1388.